package cd.zhixuan.utils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author: HuZC
 * @Description: 反射工具
 * @Date: 2022/1/10
 */
public class ReflectTools {

    /**
     * 创建新对象
     *
     * @param object   老对象
     * @param classArr 构造参数类型集合
     * @param params   构造参数集合
     * @param <T>      某类
     * @return 返回新对象
     */
    @Nullable
    public static <T> T newInstance(@NonNull Object object, Class<?>[] classArr, Object... params) {
        try {
            Class<?> clazz = object.getClass();
            return (T) newInstance(clazz, classArr, params);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    /**
     * 创建新对象
     *
     * @param clazz    某类类型
     * @param classArr 构造参数类型集合
     * @param params   构造参数集合
     * @param <T>      某类
     * @return 返回新对象
     */
    @Nullable
    public static <T> T newInstance(@NonNull Class<T> clazz, Class<?>[] classArr, Object... params) {
        try {
            Constructor<T> constructor = clazz.getDeclaredConstructor(classArr);
            return newInstance(constructor, params);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 创建新对象
     *
     * @param constructor 构造函数
     * @param params      构造参数
     * @param <T>         某类
     * @return 返回新对象
     */
    @Nullable
    public static <T> T newInstance(@NonNull Constructor<T> constructor, Object... params) {
        try {
            constructor.setAccessible(true);
            return (T) constructor.newInstance(params);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 调用某类的方法
     *
     * @param obj          某类对象
     * @param methodName   方法名
     * @param fieldClasses 参数返回类型
     * @param fields       方法需要的参数
     * @param <T>          某类
     * @return 返回值
     */
    @Nullable
    public static <T> T call(boolean declared, @NonNull Object obj, @NonNull String methodName, Class<?>[] fieldClasses, Object... fields) {
        try {
            Method method;
            Class<?> clazz = obj.getClass();
            if (declared) {
                method = clazz.getDeclaredMethod(methodName, fieldClasses);
            } else {
                method = clazz.getMethod(methodName, fieldClasses);
            }
            method.setAccessible(true);
            return (T) method.invoke(obj, fields);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @param declared  本类的非public字段
     * @param obj       某类对象
     * @param fieldName 成员变量名
     * @param <T>       某类
     * @return 获取成员变量值
     */
    @Nullable
    public static <T> T getField(boolean declared, @NonNull Object obj, @NonNull String fieldName) {
        try {
            if (fieldName.length() == 0) return null;
            Field field;
            Class<?> clazz = obj.getClass();
            if (declared) {
                field = clazz.getDeclaredField(fieldName);
            } else {
                field = clazz.getField(fieldName);
            }
            field.setAccessible(true);
            return (T) field.get(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * @param declared  本类的非public字段
     * @param obj       某类对象
     * @param fieldName 成员变量名
     * @param value     修改值
     * @return success
     */
    public static boolean setField(boolean declared, @NonNull Object obj, @NonNull String fieldName, Object value) {
        try {
            if (fieldName.length() == 0) return false;
            Field field;
            Class<?> clazz = obj.getClass();
            if (declared) {
                field = clazz.getDeclaredField(fieldName);
            } else {
                field = clazz.getField(fieldName);
            }
            field.setAccessible(true);
            field.set(obj, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }

}
